#include "font_manager.h"
#include "../core/base/file_io.h"

static KFontManager g_fontManager;



KFontManager::KFontManager()
{
	mFontRS.mBlendMode = KRenderState::eAlphaBlend;
}

KFontManager::~KFontManager()
{

}

KFontManager* KFontManager::GetInstance()
{
	return &g_fontManager;
}

bool KFontManager::Initialize()
{
	int error = FT_Init_FreeType( &mFTLibrary );
	if ( error )
		return false;

	mDefaultFontIndex = LoadAnsiCharFromFont("arial.ttf", 14);
	if (mDefaultFontIndex == -1)
		return false;
	else
		return true;
}

void KFontManager::Destory()
{
	for (int i = 0; i < (int)mAnsiCharData.size(); ++i) {
		if (mAnsiCharData[i]->texture[0])
			delete mAnsiCharData[i]->texture[0];
		if (mAnsiCharData[i]->texture[1])
			delete mAnsiCharData[i]->texture[1];
		delete mAnsiCharData[i];
	}
	mAnsiCharData.clear();
	// Cleanup for the library.
	FT_Done_FreeType(mFTLibrary);
}

#define ANSI_CHAR_TEX_SIZE (256)

int KFontManager::LoadAnsiCharFromFont(const char* fontFileName, int height)
{
	FT_Face newFace;
	KFileReader fileReader(fontFileName);
	void* pFontData = fileReader.CopyIntoMemory();

	int error = FT_New_Memory_Face( mFTLibrary, (const FT_Byte*)pFontData, fileReader.GetFileSize(), 0, &newFace );
	FT_Set_Char_Size( newFace, height << 6, height << 6, 96, 96);

	if ( error )
		return -1;

	ANSI_CHAR_DESC* charDesc = new ANSI_CHAR_DESC;
	if (height > ANSI_CHAR_TEX_SIZE) {
		delete charDesc;
		FT_Done_Face(newFace);
		return -1; // Are you mad?
	}

	charDesc->texture[0] = new KTexture;
	int fontWidth = newFace->size->metrics.x_ppem;
	int fontHeight = newFace->size->metrics.y_ppem;
	int texWidth0 = ANSI_CHAR_TEX_SIZE, texWidth1 = ANSI_CHAR_TEX_SIZE;
	int texHeight0 = 0, texHeight1 = 0;
	int gridCntX = ANSI_CHAR_TEX_SIZE / fontWidth;
	int gridCntY = ANSI_CHAR_TEX_SIZE / fontHeight;
	if (gridCntX*gridCntY < 128) {
		// We have to use two textures to store this font.
		charDesc->texture[1] = new KTexture;
		texHeight0 = ANSI_CHAR_TEX_SIZE;
		if (gridCntX*gridCntY*2 > 128) {
			delete charDesc;
			return -1;  // Two texture still cannot store this font
		}

		texHeight1 = (128 - gridCntX*gridCntY) / gridCntX;
		texHeight1 += (((128 - gridCntX*gridCntY) % gridCntX) ? 1 : 0);
		texHeight1 *= fontHeight;
		texHeight1 = NextPowerOfTwo(texHeight1);
	}
	else {
		charDesc->texture[1] = NULL;
		texHeight0 = 128 / gridCntX;
		texHeight0 += ((128 % gridCntX) ? 1 : 0);
		texHeight0 *= fontHeight;
		texHeight0 = NextPowerOfTwo(texHeight0);
	}

	bool textureCreated = true;
	if (texHeight0 > 0) {
		if (!charDesc->texture[0]->CreateFromScratch(texWidth0, texHeight0, eRGBA_L8))
			textureCreated = false;
	}

	if (texHeight1 > 0) {
		if (!charDesc->texture[1]->CreateFromScratch(texWidth1, texHeight1, eRGBA_L8)) {
			textureCreated = false;
		}
	}

	if (!textureCreated) {
		delete charDesc->texture[0];
		delete charDesc->texture[1];
		delete charDesc;
	}

	int ch_i = 0;
	int texCharCnt = gridCntX*gridCntY;
	for (; ch_i < 128; ++ch_i) {
		//The first thing we do is get FreeType to render our character
		//into a bitmap.  This actually requires a couple of FreeType commands:

		//Load the Glyph for our character.
		if(FT_Load_Glyph( newFace, FT_Get_Char_Index( newFace, ch_i ), FT_LOAD_DEFAULT ))
			break;

		//Move the face's glyph into a Glyph object.
		FT_Glyph glyph;
		if(FT_Get_Glyph( newFace->glyph, &glyph ))
			break;

		//Convert the glyph to a bitmap.
		FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 );
		FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

		//This reference will make accessing the bitmap easier
		FT_Bitmap& bitmap=bitmap_glyph->bitmap;

		KTexture* pCurrentTex = (ch_i >= texCharCnt ? charDesc->texture[1] : charDesc->texture[0]);
		int offsetX = (ch_i >= texCharCnt ? ((ch_i-texCharCnt)%gridCntX) : (ch_i%gridCntX));
		int offsetY = (ch_i >= texCharCnt ? ((ch_i-texCharCnt)/gridCntX) : (ch_i/gridCntX));
		offsetX *= fontWidth;
		offsetY *= fontHeight;

		charDesc->second_tex[ch_i] = (ch_i >= texCharCnt ? 1 : 0);
		charDesc->char_offset[ch_i].tex_rect.cx = 0;
		charDesc->char_offset[ch_i].tex_rect.cy = 0;
		charDesc->char_offset[ch_i].tex_rect.sx = offsetX;
		charDesc->char_offset[ch_i].tex_rect.sy = offsetY;
		charDesc->char_offset[ch_i].tex_rect.w = bitmap.width ? bitmap.width : (height/2);
		charDesc->char_offset[ch_i].tex_rect.h = bitmap.rows;
		charDesc->char_offset[ch_i].offset_dx = bitmap_glyph->left;
		charDesc->char_offset[ch_i].offset_dy = bitmap_glyph->top-bitmap.rows;

		if (bitmap.buffer)
			pCurrentTex->UpdateRect(offsetX, offsetY, bitmap.width, bitmap.rows, bitmap.buffer);
	}

	mAnsiCharData.push_back(charDesc);
	// Because all the characters have been backed into texture, the face can be released.
	FT_Done_Face(newFace);
	return (ch_i == 128 ? (int)(mAnsiCharData.size()-1) : -1);
}

void KFontManager::TextOut(const char* pStr, const KDrawLocal& local, int fontID) const
{
	if (fontID == -1) fontID = 0;

	KDrawManager::SetRenderState(mFontRS);
	ANSI_CHAR_DESC* pCharDesc = mAnsiCharData[fontID];
	KDrawLocal char_local = local;
	const char* ch = pStr;
	float x_pos = 0;
	while (*ch != '\0') {

		const CHAR_OFFSET& charOffset = pCharDesc->char_offset[(int)*ch];
		x_pos += charOffset.offset_dx;
		char_local.x = local.x + x_pos * local.rot.mCosTheta;
		char_local.y = local.y + charOffset.offset_dy + x_pos * local.rot.mSinTheta;
		KDrawManager::DrawSprite(
			char_local, 
			pCharDesc->texture[0], 
			charOffset.tex_rect, 
			0xff00ffff);
		++ch;
		x_pos += charOffset.tex_rect.w;
	}

}